package com.ipharma.application.services;

import java.rmi.RemoteException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Vector;

import com.ipharma.IpharmaPrs;
import com.ipharma.McdError;
import com.ipharma.McdPrs;
import com.ipharma.beans.BeansException;
import com.ipharma.beans.Helper;
import com.ipharma.config.Config;
import com.ipharma.config.FormateadorDeFechas;
import com.ipharma.config.Session;
import com.ipharma.dto.Product;
import com.ipharma.model.ContenidoDispenser;
import com.ipharma.model.DispenserLoteProducto;
import com.ipharma.service.DispenserService;
import com.ipharma.service.ProductoService;
import com.ipharma.service.ServiceException;

/**
 * A Dispenser class.
 * <P>
 * 
 * @author JorgE.H
 */
public class Dispenser extends Object {

   /**
    * Attributes
    */
   List<Product> vProducts;

   RecycleBin recycle;

  // DispensersPlus dbDisp;
   DispenserService dispenserService;

  //ProductsPlus dbProdUsr;
   ProductoService productoService;

   com.ipharma.interfaces.keyboard.Dispenser externalDispenser;

   Session theSess;

   Long dispId;

   /**
    * Constructor
    * 
    * @param pId
    *           the dispenser id
    */
   public Dispenser(Session pSess) {

      try {
         theSess = pSess;
         Config config = (Config) theSess.get(McdPrs.CONFIG);
         dispId = new Long(config.getDispenser());
         //dbDisp = new DispensersPlus();
         dispenserService = Helper.getDispenserService();
         recycle = new RecycleBin(theSess);
        // dbProdUsr = new ProductsPlus();
         productoService = Helper.getProductoService();
      } catch (Exception e) {
         McdError.add(e);
      }
   }

   /**
    * Returns the dispenser id
    * 
    * @return int dispenser id
    */
   public int getId() throws Exception {

      return dispId.intValue();
   }

   /**
    * Returns a vector with current dispenser products in DB
    * 
    * @return a Vector with Products
    */
   public List getProducts() throws Exception {

      Product l_prod;
      String ls_pdtCode = "no_code", ls_pdtDescription = "vacio", ls_canExtract, ls_dctRow, ls_dctColumn;
      int li_dctQuantity = 0, li_pdtId = 0, li_pdtMaxQty = 0;
      boolean lb_available = false;
      vProducts = new ArrayList<Product>();

      // reads products of current dispenser from DB
      List<Map<String, Object>> productos = dispenserService.getContentForUser(dispId, theSess.getUsuario().getId());
      for (Map<String, Object> map : productos) {
    	  ContenidoDispenser contenido = (ContenidoDispenser) map.get(IpharmaPrs.CONTENIDO_DISPENSER);
          if (contenido.getProductoDispenser() != null) {
             ls_pdtDescription = contenido.getProductoDispenser().getProducto().getDescripcion();
             li_pdtId = contenido.getProductoDispenser().getProducto().getId().intValue();
             li_dctQuantity = contenido.getProductoDispenser().getCantidadMinima().intValue();
             ls_pdtCode = contenido.getProductoDispenser().getProducto().getCodigo();
             li_pdtMaxQty = contenido.getProductoDispenser().getProducto().getCantidadMax().intValue();
          }
          ls_dctRow = contenido.getFila().toString();
          ls_dctColumn = contenido.getColumna().toString();
          ls_canExtract = (String) map.get(IpharmaPrs.PERTENECE);
          lb_available = ls_canExtract.equals(IpharmaPrs.SI);

          l_prod = new Product(ls_pdtDescription, li_dctQuantity, ls_pdtCode, lb_available, ls_dctRow, ls_dctColumn);
          l_prod.setId(li_pdtId);
          l_prod.setMaxQty(li_pdtMaxQty);

          // fills a vector with Product objects
          vProducts.add(l_prod);
      }
        
      // returns the vector
      return vProducts;
   }

   /**
    * Returns the product object corresponding to pCode from dispenser
    * 
    * @return a Product object or null if pCode does'nt exist in Dispenser
    */
   public Product getProduct(String pCode) throws Exception {

      for (int i = 0; i < vProducts.size(); i++) {
         Product l_prod = (Product) vProducts.get(i);
         if (l_prod.getCode().equals(pCode))
            return l_prod;
      }
      return null;
   }

   /**
    * Returns the product object corresponding to pProdId
    * 
    * @return a Product object or null if pProdId does'nt exist in DB
    */
   public Product getProduct(int pProdId) throws Exception {

      ProductoService l_pdt = Helper.getProductoService();
      Product l_prod = null;

      ContenidoDispenser contenido = l_pdt.getProduct(new Long(pProdId), dispId);
      String ls_pdtDescription = contenido.getProductoDispenser().getProducto().getDescripcion();
      int li_pdtId = contenido.getProductoDispenser().getProducto().getId().intValue();
      int li_dctQuantity = 0;/*= l_rs.getInt("dpb_quantity");*/
      String ls_dctRow = contenido.getFila().toString();
      String ls_dctColumn = contenido.getColumna().toString();
      String ls_pdtCode = contenido.getProductoDispenser().getProducto().getCodigo();
      int li_pdtMaxQty = contenido.getProductoDispenser().getProducto().getCantidadMax().intValue();

      l_prod = new Product(ls_pdtDescription, li_dctQuantity, ls_pdtCode,
            false, ls_dctRow, ls_dctColumn);
      l_prod.setId(li_pdtId);
      l_prod.setMaxQty(li_pdtMaxQty);
   
      return l_prod;
   }

   /**
    * Returns the product batch number
    * 
    * @return a batch array objects
    */
   public Batch[] getProductBatchs(int pProdId) throws SQLException {

      Batch l_batchs[];
      int i = 0;
      try {
         ProductoService l_dpb = Helper.getProductoService();

         int li_qty = l_dpb.getCantidadDispenserLoteProducto(dispId, new Long(pProdId)).intValue();
         l_batchs = new Batch[li_qty];

         List lotes = l_dpb.getLotesProducto(dispId, new Long(pProdId));
         for (Iterator iter = lotes.iterator(); iter.hasNext();) {
            DispenserLoteProducto element = (DispenserLoteProducto) iter.next();
            int li_batch = element.getLoteProducto().getNroLote().intValue();
            String ls_date = FormateadorDeFechas.getDateFormater().format(element.getLoteProducto().getFechaVencimiento());

            l_batchs[i++] = new Batch(pProdId, li_batch, ls_date);
         }
         return l_batchs;
         
      } catch (RemoteException e) {
         e.printStackTrace();
      } catch (BeansException e) {
         e.printStackTrace();
      } catch (ServiceException e) {
         e.printStackTrace();
      }
      
      return new Batch[0];
   }

   /**
    * Returns true if pUsr can extract the product
    * 
    * @return boolean
    */
   public boolean canExtractProduct(int pProdId, int pClassId, int pUsrId)
         throws Exception {

      // checks database for user permissions
      return productoService.isAssignedProduct(new Long(pProdId), new Long(pUsrId)).booleanValue();
   }

   /**
    * Returns true if pUsr can extract products from dispenser
    * 
    * @param pUsrId
    *           the user id
    * @return boolean
    */
   public boolean canExtract(int pUsrId) throws Exception {

      return dispenserService.isAssignedUser(dispId, new Long(pUsrId)).booleanValue();
   }

   /**
    * Sends basket down and opens door
    */
   @Deprecated
   public void purgeProducts() throws Exception {
	   if (externalDispenser == null){
		   externalDispenser = new com.ipharma.interfaces.keyboard.Dispenser(dispId.intValue());
	   }

      Config config = (Config)theSess.get("CONFIG");
      externalDispenser.purge(config.getIdentificadorEquipo());
   }

   /**
    * Extracts the pProd from the dispenser.
    * 
    * @param pProd
    *           product to be extracted from dispenser
    * @param pQty
    *           quantity to extract
    */
   public void doExtract(Product pProd, int pQty) throws Exception {
	  Config config = (Config)theSess.get(McdPrs.CONFIG);
	      
      Integer ls_row, ls_column;
      int li_disp = 0, li_extractQty = 0, li_qty = pQty;

      if (externalDispenser == null)
         externalDispenser = new com.ipharma.interfaces.keyboard.Dispenser(dispId.intValue());

      // loops till extract all
      // finds out product position
      List l_rs = dispenserService.getPositions(dispId, new Long(pProd.getId()));
      for (Iterator iter = l_rs.iterator(); (li_qty > 0 && iter.hasNext());) {
         ContenidoDispenser contenido = (ContenidoDispenser) iter.next();
         li_disp = contenido.getCantidad().intValue();
         ls_row = contenido.getFila();
         ls_column = contenido.getColumna();
         
         // calculates remainning quantity
         li_extractQty = Math.min(li_qty, li_disp);
         li_qty -= li_extractQty;

         // calls dispenser interface to extract product
         externalDispenser.dispense(config.getIdentificadorEquipo(), ls_row.intValue(), ls_column.intValue(), li_extractQty);

      }
            
   }

   /**
    * Returns dispensing status flag value
    * 
    * @return boolean
    */
   public boolean isDispensing() {

      return externalDispenser.isDispensing();
   }

   /**
    * Returns dispensing final status
    * 
    * @return true if succedded, false otherwise
    */
   public boolean dispensedOk() {

      return externalDispenser.dispensedOk();
   }

   /**
    * Updates the extraction in DB. The dispenser dispenses products in left to
    * right fashion
    * 
    * @param pProd
    *           product to be extracted from dispenser
    * @param pQty
    *           quantity to be extracted
    */
   public void updateExtract(Product pProd, int pQty) throws Exception {

      String ls_row, ls_column;
      int li_disp = 0, li_extractQty = 0, li_qty = pQty;

      // loops till extract all
      // finds out product position
      List l_rs = dispenserService.getPositions(dispId, new Long(pProd.getId()));
      for (Iterator iter = l_rs.iterator(); (li_qty > 0 && iter.hasNext());) {
         ContenidoDispenser contenido = (ContenidoDispenser) iter.next();
         li_disp = contenido.getCantidad().intValue();
         ls_row = contenido.getFila().toString();
         ls_column = contenido.getColumna().toString();
         
         // calculates remainning quantity
         li_extractQty = ((li_disp - li_qty) >= 0) ? li_qty : li_disp;
         li_qty -= li_extractQty;

         // updates dispenser contents in database
         dispenserService.updateContenidoDispenser(dispId, ls_row, ls_column, 
                                                   new Integer(li_disp - li_extractQty));
         
      }
      
      if (pQty > 0 && li_qty <= 0) {
         // updates product batch quantity and commits changes
         dispenserService.updateProductStock(dispId, new Long(pProd.getId()), new Integer(pQty));
//         theSess.getConn().commit();
      }
   }

   /**
    * Recharges a product quantity
    * 
    * @param pProd
    *           product to be recharged
    */
   public void recharge(Product pProd) throws Exception {

      String ls_row = pProd.getRow();
      String ls_column = pProd.getColumn();

      // updates dispenser contents in database due to extraction
      dispenserService.updateContenidoDispenser(dispId, ls_row, ls_column, 
                                                new Integer(pProd.getQuantity()));
      
   }

   /**
    * Returns the recycle bin object
    * 
    * @return a RecycleBin object
    */
   public List getRecycleBin() throws Exception {

      // returns the recyclebin object
      return recycle.getProducts();
   }

   /**
    * Returns the recycle bin product object
    * 
    * @return a Product object
    */
   public Product getRecycledProduct(String pCode) throws Exception {

      // returns the recyclebin object
      return recycle.getProduct(pCode);
   }

   /**
    * Adds a product into dispenser recycle bin
    * 
    * @param pProd
    *           the product to be added
    */
   public void addToRecycleBin(Product pProd) throws Exception {

      // adds product into recycle bin
      recycle.addProduct(pProd);
   }

   /**
    * Empties the recycle bin
    * 
    * @return the vProducts vector containing the products deleted
    */
   public Vector emptyRecycleBin() throws Exception {

      Vector vRecProducts = (Vector)((Vector) recycle.getProducts()).clone();

      // empties the recycle bin
      recycle.empty();

      return vRecProducts;
   }

}
